﻿namespace testca
{
    class Vga
    {
        int x = 0, y = 0;
        ushort base_port;
        byte background;
        Kernel.StaticArray mem;

        const int width = 80;
        const int height = 25;
        const byte Default_Background = 0x1f;
        const ulong VGA_BDA_PORT = 0x463;
        const ulong VGA_BDA_TYPE = 0x410;
        const ulong COLOUR_FRAMEBUFFER = 0xb8000;
        const ulong MONO_FRAMEBUFFER = 0xb0000;

        public Vga(ulong bda_base)
        {
            ulong fb_addr;
            byte c = ABI.MemoryOperations.PeekU1(bda_base + VGA_BDA_TYPE);
            if ((c & 0x30) == 0x30)
                fb_addr = MONO_FRAMEBUFFER;
            else
                fb_addr = COLOUR_FRAMEBUFFER;

            base_port = ABI.MemoryOperations.PeekU2(bda_base + VGA_BDA_PORT);

            mem = new Kernel.StaticArray(fb_addr, 0x1000, 2);
            background = Default_Background;
            Clear();
            UpdateCursor();
        }

        public int GetX()
        {
            return x;
        }

        public int GetY()
        {
            return y;
        }

        public void SetX(int newx)
        {
            if (newx <= 0)
                throw new System.ArgumentOutOfRangeException();
            if (newx > width)
                throw new System.ArgumentOutOfRangeException();
            x = newx;
        }

        public void SetY(int newy)
        {
            if (newy <= 0)
                throw new System.ArgumentOutOfRangeException();
            if (newy > height)
                throw new System.ArgumentOutOfRangeException();
            y = newy;
        }

        public int GetWidth()
        {
            return width;
        }

        public int GetHeight()
        {
            return height;
        }

        public void Clear()
        {
            mem.Clear((char)(((char)background << 8) | ' '));
            x = 0;
            y = 0;
        }

        public void UpdateCursor()
        {
	        ushort position = (ushort)((y * width) + x);
	        ABI.IoOperations.PortOut(base_port, (byte)0x0f);
            ABI.IoOperations.PortOut((ushort)(base_port + 1), (byte)(position & 0xff));
            ABI.IoOperations.PortOut(base_port, (byte)0x0e);
            ABI.IoOperations.PortOut((ushort)(base_port + 1), (byte)((position >> 8) & 0xff));
        }

        public void PutString(string s)
        {
            for (int i = 0; i < s.Length; i++)
                Write(s[i]);
            _putnl();
            UpdateCursor();
        }

        public void SetTextAttribute(byte attr)
        {
            background = attr;
        }

        #region IVideo Members


        public void Write(char ch)
        {
            if (ch == '\n')
                _putnl();
            else
            {
                mem.Set((ulong)(x + y * width), (char)((ch & 0xff) | (((char)background) << 8)));
                x++;
            }
        }

        public void Flush()
        {
            UpdateCursor();
        }

        #endregion

        void _putnl()
        {
            x = 0;
            y++;
        }
    }
}
